﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Caixa.Model;
using Caixa.DB;
using System.Data.Common;
using System.Collections;
using System.Data;

namespace Caixa.DAO
{
    public static class MovimentacaoDAO
    {
        #region " Methods "

        #region " Get Methods "

        public static Movimentacao GetByID(int id)
        {
            Movimentacao m = null;

            using (DAHelper db = new DAHelper())
            {
                string SQL = string.Format(@"
					SELECT 
						{1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}
					FROM {0}
					WHERE
						{1} = :{1}
				",
                    Movimentacao.Fields.TABELA,
                    Movimentacao.Fields.ID,
                    Movimentacao.Fields.IDCONTA,
                    Movimentacao.Fields.TIPO,
                    Movimentacao.Fields.MOVIMENTACAOTIPO,
                    Movimentacao.Fields.DESCRICAO,
                    Movimentacao.Fields.DATA,
                    Movimentacao.Fields.VALOR,
                    Movimentacao.Fields.SALDO
                );

                // Prepara os parâmetros da Query
                List<DbParameter> parms = new List<DbParameter>();
                parms.Add(db.GetParameter(Movimentacao.Fields.ID, id));

                // Executa a query no Banco de dados
                IEnumerator data = db.SelectSqlDr(SQL, parms.ToArray()).GetEnumerator();

                // Converte o resultado do Banco de dados em objeto
                m = DAHelper.ToModel<Movimentacao>(data);
            }

            return m;
        }

        public static List<Movimentacao> GetByData(Conta c, DateTime data)
        {
            return GetByPeriodo(c, data, data);
        }

        public static List<Movimentacao> GetByPeriodo(Conta c, DateTime dataInicio, DateTime dataFim)
        {
            List<Movimentacao> list = new List<Movimentacao>();

            // Formata a hora, minuto, segundo e milésimos para não dar problema na consulta
            dataInicio = new DateTime(dataInicio.Year, dataInicio.Month, dataInicio.Day, 0, 0, 0, 0);
            dataFim = new DateTime(dataFim.Year, dataFim.Month, dataFim.Day, 23, 59, 59, 999);

            using (DAHelper db = new DAHelper())
            {
                string SQL = string.Format(@"
					SELECT 
						{1}
					FROM {0}
					WHERE
						{2} = :{2} AND
						{3} BETWEEN :{3}Inicio AND :{3}Fim
				",
                    Movimentacao.Fields.TABELA,
                    Movimentacao.Fields.ID,
                    Movimentacao.Fields.IDCONTA,
                    Movimentacao.Fields.DATA
                );

                // Prepara os parâmetros da Query
                List<DbParameter> parms = new List<DbParameter>();
                parms.Add(db.GetParameter(Movimentacao.Fields.IDCONTA, c.ID));
                parms.Add(db.GetParameter(string.Format("{0}Inicio", Movimentacao.Fields.DATA), dataInicio, DbType.Date));
                parms.Add(db.GetParameter(string.Format("{0}Fim", Movimentacao.Fields.DATA), dataFim, DbType.Date));

                // Executa a query no Banco de dados				
                DataTable dt = db.SelectSqlDt(SQL, parms.ToArray());

                // Restaga a instâcia do objeto
                foreach (DataRow row in dt.Rows)
                {
                    int movID;
                    int.TryParse(string.Format("{0}", row[Movimentacao.Fields.ID]), out movID);
                    Movimentacao m = GetByID(movID);
                    if (m != null)
                        list.Add(m);
                }
            }

            return list;
        }

        public static double GetSaldoAnterior(Movimentacao m)
        {
            double saldo = 0;

            using (DAHelper db = new DAHelper())
            {
                string SQL = string.Format(@"
					SELECT TOP 1
						{3}
					FROM {0}
					WHERE
						({1} < {4}) 
						AND ({2} <= :{2})
					ORDER BY 
						{2} DESC, 
						{1} DESC
				",
                 Movimentacao.Fields.TABELA,
                 Movimentacao.Fields.ID,
                 Movimentacao.Fields.DATA,
                 Movimentacao.Fields.SALDO,
                 m.ID);

                // Prepara os parâmetros da Query
                List<DbParameter> parms = new List<DbParameter>();
                parms.Add(db.GetParameter(Movimentacao.Fields.DATA, m.Data, DbType.Date));

                // Executa a query no Banco de dados e retorna se foi atualizado com sucesso
                double.TryParse(string.Format("{0:0.00}", db.SelectSqlSc(SQL, parms.ToArray())), out saldo);
            }

            return saldo;
        }

        private static Dictionary<int, double> GetMovAcertarSaldo(Movimentacao m)
        {
            Dictionary<int, double> dictionary = new Dictionary<int, double>();
            dictionary.Add(m.ID, (m.Valor * m.Fator));

            using (DAHelper db = new DAHelper())
            {
                string SQL = string.Format(@"
					SELECT
						{1},
						({3} * {4}) AS {3}
					FROM {0}
					WHERE
						({1} > {5})
						AND ({2} >= :{2})
					ORDER BY 
						{2} ASC, 
						{1} ASC
				",
                 Movimentacao.Fields.TABELA,
                 Movimentacao.Fields.ID,
                 Movimentacao.Fields.DATA,
                 Movimentacao.Fields.VALOR,
                 Movimentacao.Fields.TIPO,
                 m.ID);

                // Prepara os parâmetros da Query
                List<DbParameter> parms = new List<DbParameter>();
                parms.Add(db.GetParameter(Movimentacao.Fields.DATA, m.Data, DbType.Date));

                // Executa a query no Banco de dados e retorna se foi atualizado com sucesso
                DbDataReader dr = db.SelectSqlDr(SQL, parms.ToArray());
                while (dr.Read())
                {
                    double valor;
                    double.TryParse(string.Format("{0}", dr.GetValue(dr.GetOrdinal(Movimentacao.Fields.VALOR))), out valor);
                    dictionary.Add(
                        dr.GetInt32(dr.GetOrdinal(Movimentacao.Fields.ID)),
                        valor
                    );
                }
                dr.Close();
            }

            return dictionary;
        }

        #endregion

        #region " DDL Methods "

        public static double AtualizaSaldo(Movimentacao m)
        {
            double saldoAtual = 0;

            // Obtém todas as movimentações que terão que acertar os saldos
            Dictionary<int, double> dicMovAcertar = GetMovAcertarSaldo(m);

            // Recupera o saldo da movimentação anterior a esta
            double saldoAnterior = GetSaldoAnterior(m);

            // Acerta o saldo de cada movimentação
            using (DAHelper db = new DAHelper())
            {
                foreach (KeyValuePair<int, double> movItem in dicMovAcertar)
                {
                    saldoAnterior = Math.Round(saldoAnterior + movItem.Value, 2);
                    saldoAtual = saldoAnterior;

                    string SQL = string.Format(@"
						UPDATE {0} SET
							{2} = :{2}				
						WHERE						
							{1} = {3}
					",
                     Movimentacao.Fields.TABELA,
                     Movimentacao.Fields.ID,
                     Movimentacao.Fields.SALDO,
                     movItem.Key);

                    // Prepara os parâmetros da Query
                    List<DbParameter> parms = new List<DbParameter>();
                    parms.Add(db.GetParameter(Movimentacao.Fields.SALDO, saldoAtual, DbType.Double));

                    // Executa a query no Banco de dados
                    db.ExecuteSql(SQL, parms.ToArray());
                }
            }

            return saldoAtual;
        }

        public static int Insert(Movimentacao m)
        {
            int newID = 0;

            using (DAHelper db = new DAHelper())
            {
                // Prepara os parâmetros da Query
                List<DbParameter> parms = new List<DbParameter>();
                parms.Add(db.GetParameter(Movimentacao.Fields.IDCONTA, m.IDConta));
                parms.Add(db.GetParameter(Movimentacao.Fields.TIPO, m.Fator));
                parms.Add(db.GetParameter(Movimentacao.Fields.MOVIMENTACAOTIPO, m.MovimentacaoTipo));
                parms.Add(db.GetParameter(Movimentacao.Fields.DATA, m.Data, DbType.Date));
                parms.Add(db.GetParameter(Movimentacao.Fields.DESCRICAO, m.Descricao));
                parms.Add(db.GetParameter(Movimentacao.Fields.VALOR, m.Valor, DbType.Double));
                parms.Add(db.GetParameter(Movimentacao.Fields.SALDO, m.Saldo, DbType.Double));

                // Executa a query no Banco de dados e retorna o ID do novo registro
                newID = db.InsertSql(Movimentacao.Fields.TABELA, parms.ToArray());
            }

            return newID;
        }

        public static bool Update(Movimentacao m)
        {
            bool success = false;

            using (DAHelper db = new DAHelper())
            {
                // Prepara os parâmetros da Query
                List<DbParameter> parms = new List<DbParameter>();
                parms.Add(db.GetParameter(Movimentacao.Fields.IDCONTA, m.IDConta));
                parms.Add(db.GetParameter(Movimentacao.Fields.TIPO, m.Fator));
                parms.Add(db.GetParameter(Movimentacao.Fields.MOVIMENTACAOTIPO, m.MovimentacaoTipo));
                parms.Add(db.GetParameter(Movimentacao.Fields.DATA, m.Data, DbType.Date));
                parms.Add(db.GetParameter(Movimentacao.Fields.DESCRICAO, m.Descricao));
                parms.Add(db.GetParameter(Movimentacao.Fields.VALOR, m.Valor, DbType.Double));
                parms.Add(db.GetParameter(Movimentacao.Fields.SALDO, m.Saldo, DbType.Double));

                // Define a cláusula where do Update
                string sWhere = string.Format("{0} = {1}", Movimentacao.Fields.ID, m.ID);

                // Executa a query no Banco de dados e retorna se foi atualizado com sucesso
                success = db.UpdateSql(Movimentacao.Fields.TABELA, parms.ToArray(), sWhere);
            }

            return success;
        }

        public static bool Delete(Movimentacao m)
        {
            bool success = false;

            using (DAHelper db = new DAHelper())
            {
                // Define a cláusula where do Delete
                string sWhere = string.Format("{0} = {1}", Movimentacao.Fields.ID, m.ID);

                // Executa a query no Banco de dados e retorna se o registro foi excluido com sucesso
                success = db.DeleteSql(Movimentacao.Fields.TABELA, sWhere);
            }

            return success;
        }

        #endregion

        #endregion
    }
}
